#include <fstream>
#include <stdlib.h>
#include <time.h>

#ifdef WIN32
#include <process.h>
#include <Windows.h>
#endif

#include "QueryOutput.h"

QueryPrintStructure::QueryPrintStructure(Query *query)
{
	name.first = "123456";
	name.second = query->node;

	int d = rand() / float(RAND_MAX) * 25;
	name.first[0] = d + 0x61;

	for (int i = 1; i < 6; i++)
	{
		int d = rand() / float(RAND_MAX) * 35;

		if (d < 26)
		{
			name.first[i] = d + 0x61;
		}
		else
		{
			name.first[i] = d - 26 + 0x30;
		}
	}

	for (std::vector <Query *> :: iterator i = query->lists.begin(); i != query->lists.end(); ++i)
	{
		lists.push_back(new QueryPrintStructure(*i));
	}
}

QueryPrintStructure::~QueryPrintStructure()
{
	for (std::vector <QueryPrintStructure *> :: iterator i = lists.begin(); i != lists.end(); ++i)
	{
		delete *i;
	}
}

void QueryOutput::initialize()
{
	srand(time(0));
}

QueryOutput::QueryOutput()
{
	initialize();
}

QueryOutput::~QueryOutput()
{
}

bool QueryOutput::printQuery(Query *query, std::string filename)
{
	ofile.open(filename.c_str());

	if (!ofile)
	{
		return false;
	}

	QueryPrintStructure *qps = new QueryPrintStructure(query);

	ofile << "digraph G {\n";
	ofile << qps->name.first << " [label = \"" << qps->name.second->op->getName().c_str() << "\"];\n";

	printQuery(qps);

	ofile << "}\n";

	delete qps;

	ofile.close();	

	return true;
}

bool QueryOutput::printQueries(QueryStorage *qst, std::string dir, std::string algorithmName)
{
	if (dir[dir.size() - 1] != '/')
	{
		dir += "/" + algorithmName;
	}
	else
	{
		dir += algorithmName;
	}

	dir += '/';

	bool retval = true;
	for (std::vector <QueryInfo *> :: iterator i = qst->queries.begin(); i != qst->queries.end(); ++i)
	{
		if (!printQuery(*i, dir))
		{
			retval = false;
		}
	}

	return retval;
}

bool QueryOutput::printQuery(QueryInfo *qi, std::string dir)
{
	std::string dotname = dir + "dot/";
	std::string picname = dir + "pic/";

	std::string filename;

	std::string number;
	sprintf(const_cast<char *>(number.c_str()), "%d", qi->number);

	filename = number.c_str();
	filename += ".dot";
	
	ofile.open(std::string(dotname + filename).c_str());

	if (!ofile)
	{
		return false;
	}

	QueryPrintStructure *qps = new QueryPrintStructure(qi->query);

	ofile << "digraph G {\n";
	ofile << "label = \"query #" << number.c_str() << "\\ncost = " << (qi->cost / 1000.) << " s.\";\n";
	ofile << qps->name.first << " [label = \"" << qps->name.second->op->getName().c_str();
	if (qps->name.second->op->getName().c_str() != qps->name.second->physOp->realizationName)
	{
		ofile << "\\nPhysical operator = \'" << qps->name.second->physOp->realizationName << "\'";
	}
	ofile << "\"];\n";

	printQuery(qps);

	ofile << "}\n";

	delete qps;

	ofile.close();

	visualize(dotname + filename, picname + filename);

	return true;
}

std::string QueryOutput::createCMD(std::string from, std::string to)
{
	std::string cmd = "dot -Tpng ";
	cmd += from;
	cmd += " -o ";

	if (to == "")
	{
		for (std::string::reverse_iterator i = from.rbegin(); i != from.rend(); i++)
		{
			if ((*i) == '.')
			{
				from.erase(i.base() - 1, from.end());
			}
		}

		cmd += from + ".png";
	}
	else
	{
		for (std::string::reverse_iterator i = to.rbegin(); i != to.rend(); i++)
		{
			if ((*i) == '.')
			{
				to.erase(i.base() - 1, to.end());
				break;
			}
		}

		cmd += to + ".png";
	}	

	return cmd;
}

void QueryOutput::printQuery(QueryPrintStructure *qps)
{	
	for (std::vector<QueryPrintStructure *> :: iterator i = qps->lists.begin(); i != qps->lists.end(); ++i)
	{
		std::string from = qps->name.first.c_str();
		
		std::string to = (*i)->name.first.c_str();
		
		ofile << from << "->" << to << ";\n";

		ofile << to << " [label = \"" << (*i)->name.second->op->getName().c_str();
		if ((*i)->name.second->op->getName().c_str() != (*i)->name.second->physOp->realizationName)
		{
			ofile << "\\nPhysical operator = \'" << (*i)->name.second->physOp->realizationName << "\'";
		}
		ofile << "\"];\n";

		printQuery(*i);
	}
}

void QueryOutput::visualize(std::string from, std::string to)
{
#ifdef WIN32
	//_spawnlp(P_NOWAITO, "cmd", "/c", createCMD(from, to).c_str(), 0);
#else
	system(createCMD(from, to).c_str());
#endif
}

void QueryOutput::printQuery(Json::Value &node, Query *query)
{
	node[query->node->op->getName()] = Json::Value();

	if (!query->lists.size())
	{
		node[query->node->op->getName()]["->"] = "end";
	}

	for (std::vector <Query *> :: iterator i = query->lists.begin(); i != query->lists.end(); ++i)
	{
		printQuery(node[query->node->op->getName()], *i);
	}
}

void QueryOutput::beginOutput(std::string name)
{
	std::string path = "output/" + name;
	std::string rmdir;

#ifdef WIN32
	path = "output\\" + name;
	rmdir = "rmdir /S /Q " + path;
#else
	rmdir = "rm -rf " + path;
#endif
	system(rmdir.c_str());

#ifdef WIN32
	CreateDirectory("output", 0);
#else
	system("mkdir output");
#endif

	std::string mkdir = "mkdir \"" + path;
	std::string t = mkdir + "\"";

	system(t.c_str());

	t = mkdir + "/dot\"";
	system(t.c_str());

	t = mkdir + "/pic\"";
	system(t.c_str());

	t = path + "/dot/tree.dot";
	dot.open(t.c_str());
	dot << "digraph G {\n";

	dot << "label = \"" << name << "\";\n";
}

void QueryOutput::endOutput(std::string name)
{
	dot << "}\n";
	dot.close();

	std::string path = "output/" + name;
	std::string cmd = "dot -Tpng " + path + "/dot/tree.dot -o " + path + "/pic/tree.png";
	//system(cmd.c_str());

	std::string filename = "output/output_" + name + ".txt";
	std::ofstream ofile(filename.c_str());
	ofile << root;
	ofile.close();
}

void QueryOutput::printQuery(QueryInfo *qi, int current)
{
	if (qi->number != current)
	{			
		if (current)
		{
			dot << current;
		}
		else
		{
			dot << "root";
		}
		dot << "->" << qi->number << ";\n";			
	}
	else
	{
		return;
	}

	dot << qi->number << " [label = \"query #" << qi->number << "\\ncost = " << (qi->cost / 1000.) << " s.\", fixedsize = false" << "];\n";
	
    std::string number;
    sprintf(const_cast<char *>(number.c_str()), "%d", qi->number);

	root[number.c_str()]["cost"] = qi->cost;
    printQuery(root[number.c_str()], qi->query);
}

QueryOutput& QueryOutput::operator << (std::string str)
{
	dot << str;

	return *this;
}

QueryOutput& QueryOutput::operator << (int i)
{
	dot << i;

	return *this;
}

QueryOutput& QueryOutput::operator << (double d)
{
	dot << d;

	return *this;
}

QueryOutput& QueryOutput::operator << (uint64_t ui64)
{
	dot << ui64;

	return *this;
}

void QueryOutput::printOptimizationResults(std::string name, int min_query, double min_cost, double start_cost, uint64_t time)
{
	*this << min_query << " [style = filled, color = green];\n";
	*this << "label = \"\\n" << name << "\\n\\nFastest query is query #" << min_query << " with cost = " << (min_cost / 1000.)
		<< " s.\\nElapsed time: " << (time / 1000.) << " s.\\nOptimization: " << ((1 - min_cost / start_cost) * 100) << " %\\n\\nOriginal time: " 
		<< start_cost << " ms.\\nOverall time (with optimization): " << ((min_cost + time) / 1000.) << " s.\\nOverall optimization: " 
		<< ((1 - (min_cost + time) / start_cost) * 100) << " %\";\n";
}